from marshmallow import fields

from extensions import marshmallow as ma


class SchemaBase(ma.Schema):
    class Meta:
        # json_module = _json
        # fields = ('items',)
        dateformat = '%Y/%m/%d %H:%M:%S'


from extensions import file_store


class Resource(SchemaBase):
    origin_file_name = fields.Str(dump_to='name')
    mime_type = fields.Str(dump_to='type')
    size = fields.Int()
    path = fields.Function(lambda x: x.path if x.type == 'web' else file_store.url_for(x.path), dump_to='url')
    thumb_path = fields.Function(lambda x: file_store.url_for(x.thumb_path) if x.type != 'web' else x.path,
                                 dump_to='thumb_url')

    id = fields.Str()


class Language(SchemaBase):
    id = fields.Str()
    status = fields.Str()
    type = fields.Str()
    logo = fields.Str()
    screen_name = fields.Str()


class Metadata(SchemaBase):
    id = fields.Str()
    screen_name = fields.Str()
    type = fields.Str()
    value_mode = fields.Str()
    required = fields.Boolean()
    extension = fields.Str()


class Pagination(SchemaBase):
    total = fields.Int()
    current_page = fields.Int()
    page_size = fields.Int()
    page = fields.Int(dump_to='current_page')
    per_page = fields.Int(dump_to='page_size')


class Query(SchemaBase):
    items = fields.List(fields.Dict)
    pagination = fields.Nested(Pagination)

    def jsonify_with(self, items, pagination={}):
        return super().jsonify(
            {
                'items': items,
                'pagination': pagination
            }
        )


class Currency(SchemaBase):
    id = fields.Str()
    screen_name = fields.Str()
    logo = fields.Str()
    prefix = fields.Str()


class ProductContent(SchemaBase):
    language_code = fields.Str()
    summary = fields.Str()
    detail = fields.Str()


class ProductMetadata(SchemaBase):
    id = fields.Str()
    metadata_id = fields.Str()
    name = fields.Str()
    value = fields.Str()


class LanguageAndCurrency(SchemaBase):
    language = fields.Nested(Language)
    currency = fields.Nested(Currency)


class ProductCategory(SchemaBase):
    id = fields.Str()
    screen_name = fields.Str()


class BrandBase(SchemaBase):
    id = fields.Str()
    brand_name = fields.Str()
    describe = fields.Str()
    logo = fields.Str()
    status = fields.Str()


class AdditionalItem(SchemaBase):
    id = fields.Str()

class ProductBase(SchemaBase):
    category = fields.Nested(ProductCategory)
    title = fields.Str()

    sub_title = fields.Str()
    id = fields.Str()
    brand_id = fields.Str()
    like_count = fields.Integer()
    click_count = fields.Integer()
    tags = fields.Str()
    collection_count = fields.Integer()
    cover = fields.Nested(Resource, only=['id', 'path', 'thumb_path'])


    # minimum_price = fields.Number()
    # maximum_price = fields.Number()
    # average_price = fields.Number()
    # content = fields.Function(
    #     lambda product, context: ProductContentSchema().dump(product.get_content(context.get('language', None)),
    #                                                          many=True).data)


    # metadata = fields.Method('_write_metadata')

    # def  _write_price(self, data):
    #     pass



class ProductMetadataSchema(SchemaBase):
    metadata = fields.Method('_write_metadata')

    def _write_metadata(self, data):
        return {
            'id': data.metadata.id,
            'screen_name': data.metadata.screen_name,
            'value_mode': data.metadata.value_mode,
            'value': data.value,
            'extension': data.metadata.extension
        }



class Product(ProductBase):
    contents = fields.Method(serialize='_write_contents')
    stock = fields.Int()
    type = fields.Str()
    status = fields.Str()
    gallery = fields.List(fields.Nested(Resource, only=['id', 'path', 'thumb_path']))
    brand = fields.Nested(BrandBase, only=['id', 'brand_name'])
    price = fields.Method(serialize="_write_price")
    extData = fields.List(fields.Nested(ProductMetadata), dump_to='metadata')
    # contents = fields.List(fields.Nested(ProductContent))
    def _write_price(self, data):
        minimum_price = data.minimum_price
        maximum_price = data.maximum_price
        average_price = data.average_price
        currency = self.context.get('currency', None)
        if currency:
            currency_schema = Currency()
            if currency.is_main_currency():

                return {
                    'minimum': float(round(minimum_price, 2)),
                    'maximum': float(round(maximum_price, 2)),
                    'average': float(round(average_price, 2)),
                    'currency': currency_schema.dump(currency).data
                }
            else:
                from decimal import Decimal
                rate = Decimal.from_float(currency.rate)
                return {
                    'minimum': float(round(minimum_price * rate, 2)),
                    'maximum': float(round(maximum_price * rate, 2)),
                    'average': float(round(average_price * rate, 2)),
                    'currency': currency_schema.dump(currency).data
                }

    def _write_contents(self, obj):

        if obj.contents:
            product_content_schema = ProductContent()
            contents = obj.contents
            language = self.context.get('language', None)
            if language:
                contents = list(filter(lambda c: c.language_code == language, contents))
            return dict(zip([c.language_code for c in contents],
                            [product_content_schema.dump(c).data for c in contents]))
            # return d
            # # for content in obj.contents:
            # #     contents[content.language_code] = {
            # #         'summary': content.summary,
            # #         'detail': content.detail,
            # #     }


            # price = fields.Method(serialize='_tprice')
            # member = fields.Nested(MemberSchema, dump_to='dealer', load_from='dealer')
            # count = fields.Int()
            # sales_count = fields.Int()
            # status = fields.Str(


class Member(SchemaBase):
    id = fields.Str()
    screen_name = fields.Str()
    sales_rate = fields.Number()
    head_image = fields.Nested(Resource, only=['path', 'thumb_path'])
    buyer_score = fields.Number()
    seller_score = fields.Number()
    deal_count = fields.Number()


class AdditionalItemBase(SchemaBase):
    def __init__(self, *args, **kwargs):
        SchemaBase.__init__(self, *args, **kwargs)
        self._currency_schema = Currency(many=False)

    id = fields.Str()
    amount = fields.Method(serialize='_write_amount')
    mode = fields.Str()
    screen_name = fields.Str()
    currency = fields.Nested(Currency)
    def _write_amount(self, data):
        context = self.context
        currency = self.context.get('currency', None)
        amount = None
        if data.mode == 'fixed':
            amount = float(round(data.fee * currency.rate, 2))
        if data.mode == 'rate':
            from decimal import Decimal
            total_amount = self.context.get('total_amount')
            amount = (total_amount * data.fee) * currency.rate
            amount = float(round(amount, 2))
        return amount


class CartItemCount(SchemaBase):
    count = fields.Int()

class Offer(SchemaBase):
    def __init__(self, *args, **kwargs):
        SchemaBase.__init__(self, *args, **kwargs)
        self._currency_schema = Currency(many=False)

    id = fields.Str()
    count = fields.Int()
    sales_count = fields.Int()
    status = fields.Str()
    price = fields.Method(serialize='_write_price')
    # product = fields.Nested(ProductBase)
    dealer = fields.Nested(Member)
    slug = fields.Str()
    currency = fields.Method(serialize='_write_currency')
    additional = fields.Method(serialize='_write_additional')
    def _write_currency(self, data):
        currency = self.context.get('currency', None)
        if currency:
            return self._currency_schema.dump(currency).data

    def _write_additional(self, data):
        additional_schema = AdditionalItemBase(many=True, context={
            'currency': self.context.get('currency'),
            'total_amount': data.price
        })
        return additional_schema.dump(data.additional).data
    def _write_price(self, data):
        price = data.price
        currency_schema = Currency()
        currency = self.context.get('currency', None)
        if currency:
            return float(round(price * currency.rate,2))
        return price


class TradeSchema(SchemaBase):
    id = fields.Str()
    amount = fields.Number()
    currency = fields.Nested(Currency)
class CartItemBase(SchemaBase):
    def __init__(self, *args, **kwargs):
        SchemaBase.__init__(self, *args, **kwargs)
        self._currency_schema = Currency(many=False)

    id = fields.Str()
    # product_id = fields.Str()
    offer = fields.Nested(Offer, only=['dealer','price'])
    product = fields.Nested(ProductBase)
    shop_cart_id = fields.Str()
    count = fields.Int()
    created_at = fields.DateTime()
    status = fields.Str()
    source = fields.Str()
    currency = fields.Method(serialize='_write_currency')
    additives = fields.Method(serialize='_write_additives')
    # amount = fields.Function(serialize='_write_amount')

    def _write_currency(self, data):
        currency = self.context.get('currency', None)
        if currency:
            return self._currency_schema.dump(currency).data

    def _write_additives(self, data):
        additives = data.additives or []
        currency = self.context.get('currency', None)
        price = data.offer.price
        additive_list = []
        for item in additives:
            additive_item = AdditionalItemBase(many=False,
                                               exclude=['mode'],
                                               context={'currency': currency, 'total_amount': data.offer.price })
            additive_list.append(additive_item.dump(item).data)
        return additive_list


class OrderDetailAdditional(SchemaBase):
    id = fields.Str()
    count = fields.Int()
    amount = fields.Number()
    additional = fields.Nested(AdditionalItemBase)


class OrderDetail(SchemaBase):
    id = fields.Str()
    amount = fields.Number()
    additionals = fields.List(fields.Nested(OrderDetailAdditional))
    product = fields.Nested(ProductBase)
    delivery_method = fields.Str()
    delivery_id = fields.Str()
    count = fields.Integer()


class OrderSchema(SchemaBase):
    id = fields.Str()
    description = fields.Str()
    remark = fields.Str()
    # member_id = Column(ForeignKeyreignKey(Member.id), nullable=True, doc='用户ID')
    status = fields.Str()
    fee = fields.Number()
    # real_fee = Column(Numeric(precision=15, scale=4), doc='实际手续费（由支付平台返回)', default=0, nullable=False)
    amount = fields.Number()
    # real_amount = Column(Numeric(precision=15, scale=4), doc='用户实际支付金额', nullable=False)
    # payment_method = Column(String(80), doc='支付方式', nullable=True)
    # trade_id = Column(String(100), doc='交易编号', nullable=False)
    # payment_seq = Column(String(120), doc='支付流水号', nullable=True)
    # refunded_amount = Column(Numeric(15, 4), doc='已退款金额', nullable=False, default=0.00)
    created_at = fields.DateTime()
    updated_at = fields.DateTime()
    expire_time = fields.DateTime()
    # settle_time = Column(DateTime, nullable=True, doc='结算时间')
    currency = fields.Nested(Currency)
    # notify_method = Column(Enum(NotifyMethod), doc='通知方式')
    # notify_info = Column(String(400), doc='通知信息', nullable=True)
    parent_id = fields.Str()
    details = fields.List(fields.Nested(OrderDetail))


class PaymentResult(SchemaBase):
    id = fields.Int()
    order_no = fields.Str()
    status = fields.Str()
    created_at = fields.DateTime()
    updated_at = fields.DateTime()
    credential = fields.Method(serialize='_write_credential')

    def _write_credential(self, data):
        if data.credential:
            import json
            return json.loads(data.credential)
        else:
            return {}
class Token(SchemaBase):
    access_token = fields.Str()
    user_id = fields.Str()
    user_type = fields.Str()
    refresh_token = fields.Str()
    expires = fields.Integer()


product_schema = Product()
product_list_schema = Product(many=True)
query_schema = Query()
metadata_list_schema = Metadata(many=True)
resource_schema = Resource(many=False)
language_list_schema = Language(many=True)
language_schema = Language(many=False)
offer_schema = Offer()
offer_list_schema = Offer(many=True, exclude=['product'])
token_schema = Token(many=False)
language_and_currency_schema = LanguageAndCurrency(many=False)
brand_schema_list = BrandBase(many=True)
brand_schema = BrandBase(many=False)

cart_item_schema = CartItemBase()
payment_result = PaymentResult()
cart_item_count = CartItemCount(many=False)
trade = TradeSchema(many=False)
order_list = OrderSchema(many=True)
class ParamError(ma.Schema):
    message = fields.Str()
    extra_info = fields.Dict()
    code = fields.Str()

    def __init__(self, code='909090', message='Param Error', extra={}):
        ma.Schema.__init__(self, many=False)

        self.message = message
        self.code = code
        self.extra_info = extra

    @classmethod
    def jsonify_with(cls, code=None, message=None, extra={}, *args, **kwargs):
        error = ParamError(code, message, extra)
        return error.jsonify(*args, **kwargs)

    def jsonify(self, *args, **kwargs):
        return super().jsonify({
            'message': self.message,
            'code': self.code,
            'extra_info': self.extra_info
        }, *args, **kwargs), 400


login_name_is_exist = ParamError(
    code='100000',
    message='登录名已经存在',
    extra={
        'login_name': 'login_name_exist'
    }
)
currency_not_exist = ParamError(
    code="110000",
    message='币种不存在',
    extra={
        'currency': 'currency_not_exist'

    })
member_not_exist = ParamError(
    code='2000001',
    message='用户不存在',
    extra={
        'member': 'member_not_exist'
    }
)
